package org.rsbot.bot;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.lang.reflect.Constructor;
import java.util.EventListener;
import java.util.Map;
import java.util.TreeMap;

import org.rsbot.Application;
import org.rsbot.client.Client;
import org.rsbot.client.input.Canvas;
import org.rsbot.event.EventManager;
import org.rsbot.event.events.PaintEvent;
import org.rsbot.event.events.TextPaintEvent;
import org.rsbot.gui.AccountManager;
import org.rsbot.script.Bank;
import org.rsbot.script.Calculations;
import org.rsbot.script.DynamicConstants;
import org.rsbot.script.Environment;
import org.rsbot.script.GrandExchange;
import org.rsbot.script.InputManager;
import org.rsbot.script.Interfaces;
import org.rsbot.script.Methods;
import org.rsbot.script.PaintTools;
import org.rsbot.script.ScriptHandler;
import org.rsbot.script.Skills;
import org.rsbot.script.Store;
import org.rsbot.script.randoms.antiban.BreakHandler;

public class Bot {

    private String account;
    private BotStub botStub;
    private Client client;
    public  Methods methods;
    private Component panel;
    private final PaintEvent paintEvent;
    private final TextPaintEvent textPaintEvent;
    private final EventManager eventManager;
    private BufferedImage backBuffer;
    private BufferedImage image;
    private final InputManager im;
    private RSLoader loader;
    private final ScriptHandler sh;
    private final BreakHandler bh;
    private final Map<String, EventListener> listeners;
    public boolean debugLogging = false;
    /**
     * Whether or not user input is allowed despite a script's preference.
     */
    public volatile boolean overrideInput = false;
    /**
     * Whether or not all anti-randoms are enabled.
     */
    public boolean disableRandoms = false;
    /**
     * Whether or not the login screen anti-random is enabled.
     */
    public boolean disableAutoLogin = false;
    /**
     * Whether or not rendering is enabled.
     */
    public volatile boolean disableRendering = false;
    public boolean disableAntibans = false;
    public boolean disableBreakHandler = true;
    public boolean disableFirstTimeDeath = false;
    /**
     * The singleton of skills.
     */
    public Skills skills;
    /**
     * The singleton of bank
     */
    public Bank bank;
    /**
     * The singleton of store
     */
    public Store store;
    /**
     * The singleton of Grand Exchange
     */
    public GrandExchange grandExchange;
    /**
     * The singleton of Calculations
     */
    public Calculations calculations;
    
    /**
     * The singleton of DynamicConstants
     */
    public DynamicConstants dynConstants;
    
    public Interfaces ifaces;
    
    public PaintTools paintTools;
    
    /**
     * Defines what types of input are enabled when overrideInput is false.
     * Defaults to 'keyboard only' whenever a script is started.
     */
    public volatile int inputFlags = Environment.INPUT_KEYBOARD
            | Environment.INPUT_MOUSE;

    public Bot() {
        
        methods = new Methods();
        
        bank = new Bank(this);
        calculations = new Calculations(this);
        dynConstants = new DynamicConstants(this);
        skills = new Skills(this);
        store = new Store(this);
        ifaces = new Interfaces(this);
        paintTools = new PaintTools(this);
        grandExchange = new GrandExchange(this);
        
        im = new InputManager(this);
        
        methods.init(this);

        
        sh = new ScriptHandler(this);
        bh = new BreakHandler();
        
        paintEvent = new PaintEvent();
        textPaintEvent = new TextPaintEvent();
        eventManager = new EventManager();
        listeners = new TreeMap<String, EventListener>();
        
        methods.setupMenuListener();
        
        loader = new RSLoader();
        final Dimension size = Application.getPanelSize();
        loader.setCallback(new Runnable() {

            public void run() {
                try {
                    setClient((Client) loader.getClient());
                    resize(size.width, size.height);
                    // methods.menu.setupListener();
                } catch (final Exception ignored) {
                }
            }
        });
        
        backBuffer = new BufferedImage(size.width, size.height,
                BufferedImage.TYPE_INT_RGB);
        image = new BufferedImage(size.width, size.height,
                BufferedImage.TYPE_INT_RGB);
        

    }

    public void start() {
        try {
            loader.paint(image.getGraphics());
            loader.load();
            if (loader.getTargetName() == null)
                return;
            botStub = new BotStub(loader);
            loader.setStub(botStub);
            eventManager.start();
            botStub.setActive(true);
            final ThreadGroup tg = new ThreadGroup("RSClient-" + hashCode());
            final Thread thread = new Thread(tg, loader, "Loader");
            thread.start();
        } catch (final Exception ignored) {
        }
    }

    public void stop() {
        eventManager.killThread(false);
        sh.stopScript();
        loader.stop();
        loader.destroy();
        loader = null;
    }

    public void resize(final int width, final int height) {
        backBuffer = new BufferedImage(width, height,
                BufferedImage.TYPE_INT_RGB);
        image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        // client reads size of loader applet for drawing
        loader.setSize(width, height);
        // simulate loader repaint awt event dispatch
        loader.update(backBuffer.getGraphics());
        loader.paint(backBuffer.getGraphics());
    }

    public boolean setAccount(final String name) {
        boolean exist = false;
        for (final String s : AccountManager.getAccountNames()) {
            if (s.toLowerCase().equals(name.toLowerCase())) {
                exist = true;
            }
        }
        if (exist) {
            account = name;
            return true;
        }
        account = null;
        return false;
    }

    public void setPanel(final Component c) {
        this.panel = c;
    }

    public void addListener(final Class<?> clazz) {
        final EventListener el = instantiateListener(clazz);
        listeners.put(clazz.getName(), el);
        eventManager.addListener(el);
    }

    public void removeListener(final Class<?> clazz) {
        final EventListener el = listeners.get(clazz.getName());
        listeners.remove(clazz.getName());
        eventManager.removeListener(el);
    }

    public boolean hasListener(final Class<?> clazz) {
        return clazz != null && listeners.get(clazz.getName()) != null;
    }

    public String getAccountName() {
        return account;
    }

    public Client getClient() {
        return client;
    }

    public Canvas getCanvas() {
        if (client == null)
            return null;
        return (Canvas) client.getCanvas();
    }

    public Graphics getBufferGraphics() {
        final Graphics back = backBuffer.getGraphics();
        paintEvent.graphics = back;
        textPaintEvent.graphics = back;
        textPaintEvent.idx = 0;
        eventManager.processEvent(paintEvent);
        eventManager.processEvent(textPaintEvent);
        back.dispose();
        image.getGraphics().drawImage(backBuffer, 0, 0, null);
        if (panel != null) {
            panel.repaint();
        }
        return backBuffer.getGraphics();
    }

    public BufferedImage getImage() {
        return image;
    }

    public BotStub getBotStub() {
        return botStub;
    }

    public RSLoader getLoader() {
        return loader;
    }

    public Methods getMethods() {
        return methods;
    }

    public EventManager getEventManager() {
        return eventManager;
    }

    public InputManager getInputManager() {
        return im;
    }

    public BreakHandler getBreakHandler() {
        return bh;
    }

    public ScriptHandler getScriptHandler() {
        return sh;
    }

    private void setClient(final Client cl) {
        System.out.println("yay client initialized");
        client = cl;
        client.setCallback(new CallbackImpl(this));
        sh.addRandoms();
        sh.addAntiban();
    }

    private EventListener instantiateListener(final Class<?> clazz) {
        try {
            EventListener listener;
            try {
                final Constructor<?> constructor = clazz.getConstructor(Bot.class);
                listener = (EventListener) constructor.newInstance(this);
            } catch (final Exception e) {
                listener = clazz.asSubclass(EventListener.class).newInstance();
            }
            return listener;
        } catch (final Exception ignored) {
        }
        return null;
    }
}
